Also note, there are plenty of database systems that either have no proper datetime datatype, or have primarily datetimes that include no TZ info.
In Unix-land, this data type and boatload of library functions is the operating system. The system provides and deals with local time conversion when necessary. If your application isn't very involved with time (eg. it's not a calendar or scheduling application) then it is sensible to use Unix timestamps.
This ties you to a Unix OS, which isn't usually too bad a decision since other more important things do as well. On the other hand, using your programming language or database ties you to that database or language, which is arguably worse.
Which can lead to problems as well. Basically when dealing with "local" time (DST), you need a reliable source of data and the question boils down to whether you want system administrators to keep OSes patched and up-to-date every time tzdata is updated, or whether that should be handled at the application layer.
Java chooses to bake in tzdata, and so do a number of other app-layer platforms. JS in the browser relies on the OS instead of bundling binary tzdata. Windows and a few commercial *nix platforms do not bundle tzdata either and maintain their own definitions. The crowd-sourced tzdata has demonstrated itself better than even what Microsoft ships in Windows. I don't know anyone who would claim a dataset other than tzdata is "better".
There might come a point in time where browsers have sufficiently advanced automatic patching invisible to the end user that they would be better off bundling tzdata internally rather than relying on the OS because they can guarantee their data is better in a higher percentage of cases. It all comes down to whose update system is more seamless and more likely to occur given all the external factors that come into play. (e.g., a user might have local machine privileges to apply browser updates, but not OS updates)
You don't expose the underlying long value publicly so you end up with something like the following:
private long primitiveDate;
public Date getDate() { return new Date(primitiveDate); }
-----
This prevents calling methods from doing something stupid like:
o.getDate().setTime(123455L)
to modify a date that shouldn't be editable.
private Date date;
public Date getDate() { return new Date(date.getTime()); }
private final long creationTime = System.currentTimeMillis();
so it still has advantages over making the field a Date object.