Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Parsing calendar weeks to LocalDates with DateTimeFormatter

43 views
Skip to first unread message

Tassilo Horn

unread,
Sep 7, 2016, 2:58:58 AM9/7/16
to
Hi all,

I have a

DateTimeFormatter FORMATTER_KW = DateTimeFormatter.ofPattern("ww.YYYY");

for formatting LocalDates as calendar weeks. That works fine, however I
can't use that to parse calendar week strings to LocalDates, e.g.,

FORMATTER_KW.parse("44.2016", LocalDate::from);

throws

java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor:
{WeekBasedYear[WeekFields[MONDAY,4]]=2016,
WeekOfWeekBasedYear[WeekFields[MONDAY,4]]=50},ISO of type java.time.format.Parsed
at java.time.LocalDate.from(LocalDate.java:368)
at java.time.format.Parsed.query(Parsed.java:226)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
... 63 more

Sounds like it is complaining that the date is underspecified which it
obviously is. So I'd like such a calendar week to resolve to the last
work day in that week. (The calendar weeks are estimated deliveries.)

After a bit of doc reading and experimenting, I came up with this
solution which seems to work ok but looks a bit complicated for such a
common task such as week-date conversion. So is there anything better?

// When parsing calendar weeks like "22.2016", we want to get the last work day in that week. We
// assume that everyone in every locale has a 5 days work week (don't know how to do better).
return FORMATTER_KW.parse(editorText, temporal ->
{
WeekFields weekFields = WeekFields.of(Locale.getDefault());
DayOfWeek firstDayInWeek = weekFields.getFirstDayOfWeek();
DayOfWeek lastWorkDay = firstDayInWeek.minus(4);
long weekOfWeekBasedYear = temporal.getLong(weekFields.weekOfWeekBasedYear());
long year = temporal.getLong(weekFields.weekBasedYear());
LocalDate date = LocalDate.of(0, 1, 1);
date = date.plusYears(year);
date = date.plusWeeks(weekOfWeekBasedYear);
date = date.with(lastWorkDay);
return date;
});

Bye,
Tassilo

Tassilo Horn

unread,
Sep 7, 2016, 4:12:10 AM9/7/16
to
Tassilo Horn <ts...@gnu.org> writes:

> // When parsing calendar weeks like "22.2016", we want to get
> // the last work day in that week. We assume that everyone
> // in every locale has a 5 days work week (don't know how to do
> // better).
> return FORMATTER_KW.parse(editorText, temporal ->
> {
> WeekFields weekFields = WeekFields.of(Locale.getDefault());
> DayOfWeek firstDayInWeek = weekFields.getFirstDayOfWeek();
> DayOfWeek lastWorkDay = firstDayInWeek.minus(4);
> long weekOfWeekBasedYear = temporal.getLong(weekFields.weekOfWeekBasedYear());
> long year = temporal.getLong(weekFields.weekBasedYear());
> LocalDate date = LocalDate.of(0, 1, 1);
> date = date.plusYears(year);
> date = date.plusWeeks(weekOfWeekBasedYear);
> date = date.with(lastWorkDay);
> return date;
> });

Oh, obviously StartOfWeek - 4 days is not a sane assumption for last
workday, and neither would be StartOfWeek + 4. So right now, I just use
DayOfWeek.FRIDAY.

Bye,
Tassilo

Dr J R Stockton

unread,
Sep 9, 2016, 6:40:54 PM9/9/16
to
In comp.lang.java.programmer message <87inu8g...@gnu.org>, Wed, 7
Sep 2016 08:58:48, Tassilo Horn <ts...@gnu.org> posted:

>Hi all,
>
>I have a
>
> DateTimeFormatter FORMATTER_KW = DateTimeFormatter.ofPattern("ww.YYYY");
>
>for formatting LocalDates as calendar weeks.


Firstly, you need to say, clearly, what your definition of Week
Numbering is.

You seem to be at U.Koblenz, so you should be using ISO 8601 weeks, in
which all weeks are Mon=1 to Sun=7 and Week 01 contains January 4th
(i.e. is the first week which is all or mostly in the Gregorian Year),
so the last week of the year is numbered 52 or 53; there is never a Week
00 or a Week 54.

ISO Week Numbering is used throughout the sensible part of the Gregorian
world, but not much along the Mexico-Canada borderland.

<http://web.archive.org/web/20150723023855/http://www.merlyn.demon.co.uk
/weekinfo.htm> or later, <http://web.archive.org/web/20150711063435/http
://www.merlyn.demon.co.uk/weekcalc.htm> or later, and <http://web.archiv
e.org/web/20150703002408/http://www.merlyn.demon.co.uk/js-date7.htm> or
later, might help readers; but their code is not Java.

--
(c) John Stockton, Surrey, UK. 拯merlyn.demon.co.uk Turnpike v6.05 MIME.
Merlyn Web Site < > - FAQish topics, acronyms, & links.


Dr J R Stockton

unread,
Sep 11, 2016, 6:43:19 PM9/11/16
to
In comp.lang.java.programmer message <week-2016...@ram.dialup.fu-
berlin.de>, Sat, 10 Sep 2016 01:56:51, Stefan Ram <r...@zedat.fu-
berlin.de> posted:

>Dr J R Stockton <repl...@merlyn.demon.co.uk.invalid> writes:
>>Firstly, you need to say, clearly, what your definition of Week
>>Numbering is.
>
> In VBA, there is
>
>Function DatePart
>( Interval As String,
> Date,
> [FirstDayOfWeek As VbDayOfWeek = vbSunday],
> [FirstWeekOfYear As VbFirstWeekOfYear = vbFirstJan1] )
>
> , with VbDayOfWeek containing
>
>vbUseSystemDayofWeek, vbSunday, vbMonday, vbTuesday, vbWednesday,
>vbThursday, vbFriday, and vbSaturday
>
> and VbFirstweekofyear containing
>
>vbUseSystem, vbFirstJan1, vbFirstFourDays, and vbFirstFullWeek.
>
> So, one can adjust the first day of the week and the rule
> for the first week. Impressive design, but reportedly
>
>VBA.DateTime.DatePart( "ww", #2059-12-30#, vbSunday, vbFirstFourDays )
>
> gives 53, which reportedly is wrong. It reportedly should be 1 instead.
> This reportedly is a bug that reportedly repeats every 28 years.
>
> So, in the end, one still has to program it oneself in VBA
> if one wants a bug-free implementation.


Not _oneself_; it is sufficient for one individual to have done it.

I have done it for VBscript; Java could be different. I see Java has
DateSerial, I suppose equivalent.

See, preferably using a browser which runs VBscript, in
http://web.archive.org/web/20150511204319/http://www.merlyn.demon.co.uk/
vb-date2.htm>.

The 28-year interval only applies between missing leap years; and there
are three errors in every such interval - 3 or 4 per century. Another
error repeats every 400 years; next 2101-01-02.

Microsoft have a bug fix, which that page links to, for the 3/28-year
errors.

For VBscript code written and tested in IE8, see, in the cited page, the
script section beginning with the line
Sub WeekNumJRS(Tdy, YNo, WNo, DoW) '' Tdy is CDate in; Y W D out

Any code giving the ISO 8601 Week Number for a date needs to give also
the year number; and it might as well give also the day-of-week number
(Sunday=7, as earlier established in the Book of Genesis).

If you produce corresponding well-tested similar code in Java, I'd be
pleased to have a URL for it that I may cite. E-address implied by
signature + headers.

--
(c) John Stockton, near London. Mail ?.?.Stoc...@physics.org
Web < > - FAQish topics, acronyms, and links.

Tassilo Horn

unread,
Sep 12, 2016, 3:19:02 AM9/12/16
to
Dr J R Stockton <repl...@merlyn.demon.co.uk.invalid> writes:

Hi,

>>I have a
>>
>> DateTimeFormatter FORMATTER_KW = DateTimeFormatter.ofPattern("ww.YYYY");
>>
>>for formatting LocalDates as calendar weeks.
>
>
> Firstly, you need to say, clearly, what your definition of Week
> Numbering is.
>
> You seem to be at U.Koblenz,

Not anymore but...

> so you should be using ISO 8601 weeks, in which all weeks are Mon=1 to
> Sun=7 and Week 01 contains January 4th (i.e. is the first week which
> is all or mostly in the Gregorian Year), so the last week of the year
> is numbered 52 or 53; there is never a Week 00 or a Week 54.

...that's the week numbering definition we are using, yes.

> ISO Week Numbering is used throughout the sensible part of the
> Gregorian world, but not much along the Mexico-Canada borderland.

As far as I can tell, we don't have a customer in the Mexico-Canada
borderland yet.
No problem in not being Java, and it's definitely nice how to do these
calculations "by hand". Yet my question was if there was some standard
way to come from a calendar week (ISO 8601) to a LocalDate other than
the snippet I've posted.

Bye,
Tassilo

Dr J R Stockton

unread,
Sep 13, 2016, 6:41:34 PM9/13/16
to
In comp.lang.java.programmer message <8760q1f...@gnu.org>, Mon, 12
Sep 2016 09:18:51, Tassilo Horn <ts...@gnu.org> posted:

>No problem in not being Java, and it's definitely nice how to do these
>calculations "by hand". Yet my question was if there was some standard
>way to come from a calendar week (ISO 8601) to a LocalDate other than
>the snippet I've posted.

Well, clearly one cannot properly convert a Week to a Day!

This VBscript should be easy, I guess, to convert to Java, and writes
"2016-09-13" which is correct.


function YearMonthDay(YWD) '' ISO 8601
dim Y, M, D, FiM, SoY
Y = Left(YWD, 4) : W = Mid(YWD, 7, 2) : D = Right(YWD, 1)
SoY = DateSerial(Y, 1, 1)
FiM = SoY + 3 - (SoY+1) mod 7
YearMonthDay = FiM + 7*(W-1) + D-1
end function

document.write(YearMonthDay("2016-W37-2"))

The function is well-tested, and in the page I cited.

SoY stands for Start of (Gregorian) Year, and I think FiM stands for
First Monday. FiM will be in the range SoY-3 to SoY+3. Adjustment will
probably be needed if the zero dates of VBS & Java are not the same day
of the week.

YearMonthDay returns a VBS CDate, which I suppose to be similar to a
Java LocalDate.


Note : in VBS, there is a standard, Microsoft-documented way using
DatePart to get an ISO 8601 week number from a Date (and there might be
an inverse documented). There is a minor problem, IIRC, that it does
not return the year number; and a major one that it gives an incorrect
result for three days per 28 years. If they do have an inverse, I would
not trust it until verified for at least every day in 400 years.

Lew

unread,
Sep 13, 2016, 8:50:23 PM9/13/16
to
Dr J R Stockton wrote:
> Any code giving the ISO 8601 Week Number for a date needs to give also
> the year number; and it might as well give also the day-of-week number
> (Sunday=7, as earlier established in the Book of Genesis).

The Book of Genesis did not establish Sunday as day seven. Per the Jewish religion, the Sabbath is Saturday, implying Saturday=7.

--
Lew

Dr J R Stockton

unread,
Sep 17, 2016, 6:45:25 PM9/17/16
to
In comp.lang.java.programmer message <d704e04a-a4ec-4b93-bc18-4a942d0707
a...@googlegroups.com>, Tue, 13 Sep 2016 17:50:14, Lew
<lewb...@gmail.com> posted:
ISO weeks by definition use the Gregorian Calendar, authorised by Pope
Gregory XIII.

The ISO 8601 definition is that ISO Week 1 contains the first Thursday
of the Gregorian Calendar Year, which is equivalent to saying that Week
1 is the first ISO Week which lies mostly within the G C Y. Users of
non-Gregorian calendars could use an analogous definition.

I believe that Anno Mundi Day 2 was (mostly) Julian BC 3761/10/07, a
Monday.
0 new messages